package com.example.kotlinstudy.xiechen

import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.example.kotlinstudy.R
import com.example.kotlinstudy.XLog
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

/**
 * 配合Retrofit的优化
 * https://mp.weixin.qq.com/s/cKuGN610Fsy3hYVPuDptfw
 *
 *
 * //http://www.zyiz.net/tech/detail-133087.html
//https://github.com/idisfkj/android-api-analysis
 */
//协程coroutine

//Kotlin的协程玩了个小心机在这里，就是对线程池做了点封装，让你用的更爽而已。
//异步编程的同步写法，只是一个语法糖。

class CoroutineActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_xie_cheng)
        /*****************************************************************************************************************
         * 1.第一种方式 launch
         */
//        firstCoroutineDemo0()

        /****************************************************************************************************************
         * 2.第二种方式 aysnc
         * aysnc()函数与launch()函数相同，唯一的区别是它有返回值，因为async {}返回的是 Deferred 类型。
         * 获取async {}的返回值需要通过await()函数，它也是是个挂起函数，调用时会挂起当前协程直到 async 中代码执行完并返回某个值。
         */
//        val deferred: Deferred<String> = firstCoroutineDemo2()
//        GlobalScope.launch(Dispatchers.Main) {
//            XLog.e("线程在：" + Thread.currentThread().name)
//            val h = deferred.await()
//            XLog.e("await后面的代码会被挂起-----异步返回的值：$h")
//        }

        /*****************************************************************************************************************
         * 3.第三种方式  使用withContext
         * withContext {}不会创建新的协程，在指定协程上运行挂起代码块，并挂起该协程直至代码块运行完成。
         */
//        GlobalScope.async(Dispatchers.IO) { //指定在哪个线程，就在哪个线程
//            withContext(coroutineContext) {
//                delay(3000)
//                XLog.e("A协程：-------------->${Thread.currentThread().name}")
//            }
//        }

        /*****************************************************************************************************************
         * 4.测试挂起函数 -挂起时间
         */
        //testSuspend()

        /*****************************************************************************************************************
         * 5.suspend  不会卡主线程，完事后会调用方法，不像java要写很多回调
         */
//        runBlocking {
//            launch {
//                checkNetwork()      //加了suspend的方法只能在，协程中调用，否则会报错。
//                makeNetworkRequest()
//            }
//            XLog.e("这里先执行,不会卡主线程，")
//        }
//        XLog.e("完成！")

        /*****************************************************************************************************************
         * 6.lifecycleScope  协程的执行顺序
         * GlobalScope是生命周期是process级别的，即使Activity或Fragment已经被销毁，协程仍然在执行。所以需要绑定生命周期。
         * lifecycleScope只能在Activity、Fragment中使用，会绑定Activity和Fragment的生命周期
         * viewModelScope只能在ViewModel中使用，绑定ViewModel的生命周期
         */
//        testCoroutine()


//

    }

    /*****************************************************************************************************************
    7.挂起和阻塞和区别
     */
    fun click01(view: View) {
//        GlobalScope.launch(Dispatchers.Main) {
//            1.//挂起12s  对界面没有影响
//            delay(12000)
//            XLog.e("--->${Thread.currentThread().name} delay")
//        }

        //2.阻塞  按钮不会弹起来
        Thread.sleep(12000)
        XLog.e("--->${Thread.currentThread().name} sleep ")

    }

    /*****************************************************************************************************************
    8.join和await  cancelAndJoin
     */
    fun click02(view: View) {
        //1.launch的方式需要join的方式
//           lifecycleScope.launch{
//               var job1 = launch { delay(2000);Log.d("join和await","one") }
//               job1.join() //加了join会一直等待job1完成再执行后面的协程
//               var job2 = launch { delay(100);Log.d("join和await","two")  }
//               var job3 = launch { delay(100);Log.d("join和await","thee") }
//           }

        //2.async的方式需要await的方式
        /*   lifecycleScope.launch {
               var job1 = async { delay(2000);Log.d("join和await", "one") }
               job1.await() //async是采用await会一直等待job1完成再执行后面的协程,然后返回一个执行结果
               var job2 = async { delay(100);Log.d("join和await", "two") }
               var job3 = async { delay(100);Log.d("join和await", "thee") }
           }*/

        //3.组合并发
//        testSuspend()
//        testSuspend1() //协程的结构化并发，比testSuspend快一半的时间

        //4.取消job
        runBlocking {
            val job1 = GlobalScope.launch {
                try {
                    delay(1000)
                    Log.e("coroutine", "job1")
                } catch (e: Exception) {
                    e.printStackTrace()
                    Log.e("coroutine", "异常：${e.message}")
                }
            }
            delay(200)
            //CancellationException("取消")是自己定义，也可以不写有默认值
            //job1.cancel(CancellationException("取消")) //这里取消了，会报一个异常是看不见的，静默处理掉了。 要用try/catch捕获
            // job1.join()//join等待完成
            job1.cancelAndJoin()//上面两个函数，可以合并起来 （cancle发出指令，join执行指令）
        }

    }


    private suspend fun doOne() {
        TODO("Not yet implemented")
    }

    private fun testCoroutine() {
        val TAG = "------->"
        lifecycleScope.launch {
            delay(1000)
            Log.d(TAG, "test: " + Thread.currentThread().name)
            Log.d(TAG, "test: 协程结束")
            Toast.makeText(this@CoroutineActivity, "协程结束", Toast.LENGTH_SHORT).show()
        }
        Log.d(TAG, "test: 方法结束")
    }

    private suspend fun makeNetworkRequest() {
        delay(1000)
        XLog.e("A协程：---- 网络请求失败，无网络连接");
    }

    private suspend fun checkNetwork() {  // 有 挂起函数的 方法上都要加suspend
        withContext(Dispatchers.IO) {
            delay(3000)
            XLog.e("A协程：---- 在正检查网络，无网络连接");
            delay(100)
            XLog.e("A协程：---- 网络连接 失败");
        }

    }

    fun firstCoroutineDemo0() {
//   使用  GlobalScope.launch  创建一个协程 ， GlobalScope是一个顶级协程
        GlobalScope.launch {
            delay(3000)  //相当于java中Thread.sleep(..)
            XLog.e("A协程：------- ${Thread.currentThread().name}")//3秒后执行到这里
            //子线程中此处不可以做UI操作
            //Toast.makeText(this@MainActivity, "GlobalScope开启协程", Toast.LENGTH_SHORT).show()

            //任务调度器 切换到主线程  还可以返回结果  异步任务同步化。
            val content = withContext(Dispatchers.Main) {
                XLog.e("A协程：withContext------- ${Thread.currentThread().name}")
                Toast.makeText(this@CoroutineActivity, "协程中切换线程", Toast.LENGTH_SHORT).show()
                "sucess"
            }
        }
        XLog.e("A协程:这是主线程方法")
//        CoroutineScope(Dispatchers.Mian)

    }

    fun firstCoroutineDemo2(): Deferred<String> {
        return GlobalScope.async {
            delay(5000)
            "hello"
        }
    }

    fun testSuspend() = runBlocking {
        val measureTimeMillis = measureTimeMillis {
            val one = doJob1()
            XLog.e("第一个的值$one")
            val two = doJob2()
            XLog.e("第一个的值$two")
        }
        XLog.e("运行一共时间 ${measureTimeMillis}毫秒")

    }

    fun testSuspend1() = runBlocking {
        val measureTimeMillis = measureTimeMillis {
            val one = async { doJob1() }
            val two = async { doJob2() }
            XLog.e("值为：${(one.await() + two.await())}")
        }
        XLog.e("运行一共时间 ${measureTimeMillis}毫秒")

    }

    suspend fun doJob1(): Int {
        XLog.e("Doing Job1 ...")
        delay(1000L) // 此处模拟我们的工作代码
        XLog.e("Job1 Done")
        return 10
    }

    suspend fun doJob2(): Int {
        XLog.e("Doing Job2 ...")
        delay(1000L) // 此处模拟我们的工作代码
        XLog.e("Job2 Done")
        return 20
    }


}
